package org.chartsy.chatsy.chat.component.tabbedPane; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Font; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.chartsy.chatsy.chat.util.FontLoader; import org.chartsy.chatsy.chat.util.ModelUtil; import org.chartsy.chatsy.chat.util.SwingWorker; import org.chartsy.chatsy.chat.util.log.Log; import org.openide.util.ImageUtilities; public class ChatsyTabbedPane extends JPanel { private static final String NAME = "ChatsyTabbedPane"; private List<ChatsyTabbedPaneListener> listeners = new ArrayList<ChatsyTabbedPaneListener>(); private JTabbedPane pane = null; private Icon closeInactiveButtonIcon; private Icon closeActiveButtonIcon; private boolean closeEnabled = false; private int dragTabIndex = -1; public static final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR); public static final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); public ChatsyTabbedPane() { this(JTabbedPane.TOP); } public ChatsyTabbedPane(final int type) { setLayout(new BorderLayout()); setOpaque(false); pane = new JTabbedPane(type); pane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); add(pane); ChangeListener changeListener = new ChangeListener() { public void stateChanged(ChangeEvent changeEvent) { JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource(); int index = sourceTabbedPane.getSelectedIndex(); if (index >= 0) fireTabSelected(getTabAt(index), getTabAt(index).getComponent(), index); } }; pane.addChangeListener(changeListener); closeInactiveButtonIcon = ImageUtilities.loadImageIcon("org/chartsy/chatsy/resources/close_white.png", true); closeActiveButtonIcon = ImageUtilities.loadImageIcon("org/chartsy/chatsy/resources/close_dark.png", true); } public ChatsyTab getTabContainingComponent(Component component) { for (Component comp : pane.getComponents()) { if (comp instanceof ChatsyTab) { ChatsyTab tab = (ChatsyTab) comp; if (tab.getComponent() == component) return tab; } } return null; } public ChatsyTab addTab(String title, Icon icon, final Component component) { return addTab(title, icon, component, null); } public ChatsyTab addTab(String title, Icon icon, final Component component, String tip) { final ChatsyTab chatsyTab = new ChatsyTab(this, component); TabPanel tabpanel = new TabPanel(chatsyTab, title, icon); pane.addTab(title, null, chatsyTab, tip); pane.setTabComponentAt(pane.getTabCount() - 1, tabpanel); fireTabAdded(chatsyTab, component, getTabPosition(chatsyTab)); return chatsyTab; } public ChatsyTab getTabAt(int index) { return ((ChatsyTab) pane.getComponentAt(index)); } public int getTabPosition(ChatsyTab tab) { return pane.indexOfComponent(tab); } public Component getComponentInTab(ChatsyTab tab) { return tab.getComponent(); } public void setIconAt(int index, Icon icon) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; panel.setIcon(icon); } } public void setTitleAt(int index, String title) { if (index > 0) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; panel.setTitle(title); } } } public void setTitleColorAt(int index, Color color) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; panel.setTitleColor(color); } } public void setTitleBoldAt(int index, boolean bold) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; panel.setTitleBold(bold); } } public void setTitleFontAt(int index, Font font) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; panel.setTitleFont(font); } } public Font getDefaultFontAt(int index) { Component com = pane.getTabComponentAt(index); if (com instanceof TabPanel) { TabPanel panel = (TabPanel) com; return panel.getDefaultFont(); } return null; } public String getTitleAt(int index) { return pane.getTitleAt(index); } public int getTabCount() { return pane.getTabCount(); } public void setSelectedIndex(int index) { pane.setSelectedIndex(index); } public int indexOfComponent(Component component) { for (Component comp : pane.getComponents()) { if (comp instanceof ChatsyTab) { ChatsyTab tab = (ChatsyTab) comp; if (tab.getComponent() == component) return pane.indexOfComponent(tab); } } return -1; } public Component getComponentAt(int index) { return ((ChatsyTab) pane.getComponentAt(index)).getComponent(); } public Component getTabComponentAt(int index) { return pane.getTabComponentAt(index); } public Component getTabComponentAt(ChatsyTab tab) { return pane.getTabComponentAt(indexOfComponent(tab)); } public Component getSelectedComponent() { if (pane.getSelectedComponent() instanceof ChatsyTab) { ChatsyTab tab = (ChatsyTab) pane.getSelectedComponent(); return tab.getComponent(); } return null; } public void removeTabAt(int index) { pane.remove(index); } public int getSelectedIndex() { return pane.getSelectedIndex(); } public void setCloseButtonEnabled(boolean enable) { closeEnabled = enable; } public void addChatsyTabbedPaneListener(ChatsyTabbedPaneListener listener) { listeners.add(listener); } public void removeChatsyTabbedPaneListener(ChatsyTabbedPaneListener listener) { listeners.remove(listener); } public void fireTabAdded(ChatsyTab tab, Component component, int index) { final Iterator list = ModelUtil.reverseListIterator(listeners.listIterator()); while (list.hasNext()) ((ChatsyTabbedPaneListener) list.next()).tabAdded(tab, component, index); } public JPanel getMainPanel() { return this; } public void removeComponent(Component comp) { int index = indexOfComponent(comp); if (index != -1) removeTabAt(index); } public void fireTabRemoved(ChatsyTab tab, Component component, int index) { final Iterator list = ModelUtil.reverseListIterator(listeners.listIterator()); while (list.hasNext()) ((ChatsyTabbedPaneListener) list.next()).tabRemoved(tab, component, index); } public void fireTabSelected(ChatsyTab tab, Component component, int index) { final Iterator list = ModelUtil.reverseListIterator(listeners.listIterator()); while (list.hasNext()) ((ChatsyTabbedPaneListener) list.next()).tabSelected(tab, component, index); } public void allTabsClosed() { final Iterator list = ModelUtil.reverseListIterator(listeners.listIterator()); while (list.hasNext()) ((ChatsyTabbedPaneListener) list.next()).allTabsRemoved(); } public void close(ChatsyTab chatsyTab) { int closeTabNumber = pane.indexOfComponent(chatsyTab); pane.removeTabAt(closeTabNumber); fireTabRemoved(chatsyTab, chatsyTab.getComponent(), closeTabNumber); if (pane.getTabCount() == 0) allTabsClosed(); } private final class TabPanel extends JPanel { private final Font defaultFont = FontLoader.getDroidFont(Font.PLAIN, 11); private final Font tabFont = FontLoader.getDroidFont(Font.BOLD, 14); private final Color defaultColor = Color.decode("0x111111"); private JLabel iconLabel; private JLabel titleLabel; public TabPanel(final ChatsyTab chatsyTab, String title, Icon icon) { setLayout(new BorderLayout()); setOpaque(false); titleLabel = new JLabel(title); titleLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); titleLabel.setFont(defaultFont); titleLabel.setForeground(defaultColor); if (icon != null) { iconLabel = new JLabel(icon); add(iconLabel, BorderLayout.WEST); } add(titleLabel, BorderLayout.CENTER); if (closeEnabled) { final JLabel tabCloseButton = new JLabel(closeInactiveButtonIcon); tabCloseButton.setCursor(HAND_CURSOR); tabCloseButton.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent mouseEvent) { tabCloseButton.setIcon(closeActiveButtonIcon); } public void mouseExited(MouseEvent mouseEvent) { tabCloseButton.setIcon(closeInactiveButtonIcon); } public void mousePressed(MouseEvent mouseEvent) { final SwingWorker closeTimerThread = new SwingWorker() { public Object construct() { try { Thread.sleep(100); } catch (InterruptedException e) { Log.error(e); } return true; } public void finished() { close(chatsyTab); } }; closeTimerThread.start(); } }); add(tabCloseButton, BorderLayout.EAST); } else { setTitleFont(tabFont); if (iconLabel != null) iconLabel.setIcon(null); } } public Font getDefaultFont() { return defaultFont; } public Color getDefaultColor() { return defaultColor; } public void setIcon(Icon icon) { iconLabel.setIcon(icon); } public void setTitle(String title) { titleLabel.setText(title); } public void setTitleColor(Color color) { titleLabel.setForeground(color); } public void setTitleBold(boolean bold) { Font oldFont = titleLabel.getFont(); Font newFont; if (bold) newFont = new Font(oldFont.getFontName(), Font.BOLD, oldFont.getSize()); else newFont = new Font(oldFont.getFontName(), Font.PLAIN, oldFont.getSize()); titleLabel.setFont(newFont); } public void setTitleFont(Font font) { titleLabel.setFont(font); } } public void enableDragAndDrop() { final DragSourceListener dragSourceListener = new DragSourceListener() { public void dragDropEnd(DragSourceDropEvent event) { dragTabIndex = -1; } public void dragEnter(DragSourceDragEvent event) { event.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop); } public void dragExit(DragSourceEvent event) { } public void dragOver(DragSourceDragEvent event) { } public void dropActionChanged(DragSourceDragEvent event) { } }; final Transferable transferable = new Transferable() { private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME); public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { return pane; } public DataFlavor[] getTransferDataFlavors() { DataFlavor[] f = new DataFlavor[1]; f[0] = this.FLAVOR; return f; } public boolean isDataFlavorSupported(DataFlavor flavor) { return flavor.getHumanPresentableName().equals(NAME); } }; final DragGestureListener dragGestureListener = new DragGestureListener() { public void dragGestureRecognized(DragGestureEvent event) { dragTabIndex = pane.indexAtLocation(event.getDragOrigin().x, event.getDragOrigin().y); try { event.startDrag(DragSource.DefaultMoveDrop, transferable, dragSourceListener); } catch(Exception ex) { } } }; final DropTargetListener dropTargetListener = new DropTargetListener() { public void dragEnter(DropTargetDragEvent event) { } public void dragExit(DropTargetEvent event) { } public void dragOver(DropTargetDragEvent event) { } public void drop(DropTargetDropEvent event) { int dropTabIndex = getTargetTabIndex(event.getLocation()); moveTab(dragTabIndex,dropTabIndex); } public void dropActionChanged(DropTargetDragEvent event) { } }; DropTarget dropTarget = new DropTarget(pane, DnDConstants.ACTION_COPY_OR_MOVE, dropTargetListener, true); DragSource dragSource = new DragSource(); dragSource.createDefaultDragGestureRecognizer(pane, DnDConstants.ACTION_COPY_OR_MOVE, dragGestureListener); } private void moveTab(int prev, int next) { if (next < 0 || prev == next) return; Component cmp = pane.getComponentAt(prev); Component tab = pane.getTabComponentAt(prev); String str = pane.getTitleAt(prev); Icon icon = pane.getIconAt(prev); String tip = pane.getToolTipTextAt(prev); boolean flg = pane.isEnabledAt(prev); int tgtindex = prev > next ? next : next - 1; pane.remove(prev); pane.insertTab(str, icon, cmp, tip, tgtindex); pane.setEnabledAt(tgtindex, flg); if (flg) pane.setSelectedIndex(tgtindex); pane.setTabComponentAt(tgtindex, tab); } private int getTargetTabIndex(Point point) { Point tabPt = SwingUtilities.convertPoint(pane, point, pane); boolean isTB = pane.getTabPlacement() == JTabbedPane.TOP || pane.getTabPlacement() == JTabbedPane.BOTTOM; for (int i = 0; i < getTabCount(); i++) { Rectangle r = pane.getBoundsAt(i); if(isTB) r.setRect(r.x-r.width/2, r.y, r.width, r.height); else r.setRect(r.x, r.y-r.height/2, r.width, r.height); if (r.contains(tabPt)) return i; } Rectangle r = pane.getBoundsAt(getTabCount()-1); if(isTB) r.setRect(r.x+r.width/2, r.y, r.width, r.height); else r.setRect(r.x, r.y+r.height/2, r.width, r.height); return r.contains(tabPt)?getTabCount():-1; } }